package com.conversationboard.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Date;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.conversationboard.cache.BannedUserCache;
import com.conversationboard.config.Configuration;
import com.conversationboard.controller.ipaddress.IPAddress;
import com.conversationboard.formatter.Formatter;
import com.conversationboard.logger.Logger;
import com.conversationboard.mobile.MobileDevice;
import com.conversationboard.model.Board;
import com.conversationboard.model.Boards;
import com.conversationboard.model.Message;
import com.conversationboard.model.MessageFloodChecker;
import com.conversationboard.model.NoSuchThreadException;
import com.conversationboard.model.Thread;
import com.conversationboard.model.User;
import com.conversationboard.model.exceptions.DuplicateMessageException;
import com.conversationboard.validate.bbcode.Validator;

public class NewAnswer {

	private boolean ignoreXPosts = false;
	private String adminId = null;


	public boolean isIgnoreXPosts() {
		return ignoreXPosts;
	}


	public void setIgnoreXPosts(boolean ignoreXPosts) {
		this.ignoreXPosts = ignoreXPosts;
	}


	public String getAdminId() {
		return adminId;
	}


	public void setAdminId(String adminId) {
		this.adminId = adminId;
	}


	private boolean hasXPosts(Thread thread, int originalMessageCount) {
		return (thread.getMessageCount() > originalMessageCount);
	}


	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		response.setCharacterEncoding("UTF-8");
		request.setCharacterEncoding("UTF-8");

		String referrer = request.getHeader("referer");
		String sessionKey = request.getParameter("sKey");
		String additionalParameters = request.getParameter("additionalParameters");

		boolean mobileInterface = (additionalParameters != null);

		Principal principal = request.getUserPrincipal();
		String yourName = "";
		User user = null;

		if (principal != null) {
			yourName = principal.getName().toLowerCase();
		} else {

			/* If there is no user, the board must support unregistered posting */

			user = User.getAnonymousUser();

			String displayName = request.getParameter("displayname");
			displayName = Formatter.escapeHtml(displayName);

			if (displayName.length() > 80) {
				displayName = displayName.substring(0, 79);
			}

			if (displayName.equals("Anonymous") || displayName.equals("")) {
				request.setAttribute("message", "Please set your display name.");
				RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
				dispatcher.forward(request, response);
				return;
			}

			/* Add (Unregistered) on to user name */

			String unregisteredTag = " (unregistered)";

			String fullName = displayName + unregisteredTag;

			if (fullName.length() > 80) {
				fullName = displayName.substring(0, (80 - unregisteredTag.length()));
				fullName = fullName + unregisteredTag;
			}

			user.setDisplayName(fullName);
			user.setLoginId(IPAddress.getIPAddress(request));
		}

		String ipAddress = IPAddress.getIPAddress(request);

		if (this.getAdminId() == null) {
			String adminId = request.getParameter("adminId");
			this.setAdminId(adminId);
		}

		if ((adminId != null) && (adminId.trim().equals(""))) {
			adminId = null;
		}

		/* Ensure we're posting from this site */

		if (request.getSession() != null) {
			if (!sessionKey.equals(request.getSession().getId())) {
				return;
			}
		} else {
			return;
		}

		int boardId = Integer.parseInt(request.getParameter("boardId"));
		int threadId = Integer.parseInt(request.getParameter("threadId"));
		String text = (String) request.getParameter("text").trim();
		Date now = new Date();

		try {

			if (user == null) {
				user = User.get(yourName);
			}

			user.setIpAddress(ipAddress);

			Board board = Boards.getBoard(boardId);
			Thread thread = Thread.get(boardId, threadId);

			/*
			 * If it's not an unregistered board and the user isn't logged in
			 * then don't let them post
			 */

			if ((!user.isLoggedIn()) && board.isRequireRegistration()) {
				return;
			}

			/*
			 * Is the user banned from the board or the thread? If so, throw
			 * them out
			 */

			if (BannedUserCache.isBanned(boardId, false, yourName, ipAddress)) {
				request.setAttribute("message", "You have been barred.");
				RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
				dispatcher.forward(request, response);
				return;
			}

			if (thread.getBannedUsers().contains(yourName)) {
				request.setAttribute("message", "You have been barred from posting on this thread.");
				RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
				dispatcher.forward(request, response);
				return;
			}

			/* Is the thread locked? */

			if (thread.isLocked()) {
				request.setAttribute("message", "An administrator has locked this thread.");
				RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
				dispatcher.forward(request, response);
				return;
			}

			/* Are you authorised to post on this board? */

			if (!board.isPostable(user)) {
				RequestDispatcher dispatcher = request.getRequestDispatcher("/index.jsp");
				dispatcher.forward(request, response);
				return;
			}

			/* Trim messages that are too long */

			if (text.length() > Configuration.getInstance().getMaxMessageSize()) {
				text = text.substring(0, Configuration.getInstance().getMaxMessageSize());
			}

			/* Similarly, check for messages that are of zero length */

			if (text.length() == 0) {
				request.setAttribute("message", "Please enter a message.");
				RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
				dispatcher.forward(request, response);
				return;
			}

			/*
			 * Check for malformed bbcode. If so, forward to thread.jsp. Set
			 * bbCodeError* attributes for extraction and formatting later.
			 */

			request.setAttribute("bbCodeError", false);
			String ignoreBbCodeErrors = request.getParameter("ignoreBbCodeErrors");

			/*
			 * If ignoreBbCodeErrors isn't explicitly set to "on", check for
			 * errors
			 */

			if (ignoreBbCodeErrors == null || !ignoreBbCodeErrors.equals("on")) {
				ArrayList<String> isValidBbCode = Validator.validate(text, true); // true

				if (isValidBbCode != null) {
					String url = "/ThreadSelectedControllerServlet?boardid=" + boardId + "&threadid=" + threadId;
					RequestDispatcher dispatcher = request.getRequestDispatcher(url);
					request.setAttribute("body", text);
					request.setAttribute("bbCodeError", true);
					request.setAttribute("bbCodeErrorExplanation", isValidBbCode.get(0));
					request.setAttribute("bbCodeErrorPreContext", isValidBbCode.get(1));
					request.setAttribute("bbCodeErrorPostContext", isValidBbCode.get(2));
					dispatcher.forward(request, response);
					return;
				}
			}

			/*
			 * Check for X Posts. If there are X Posts, forward back to the
			 * thread.jsp page, setting the xpost flag and passing in the
			 * original message count. This could screw up in a minor way if a
			 * thread has been deleted in between you reading and posting, but
			 * it hardly matters. Ignore x-posts if you're posting from a mobile
			 * device.
			 */

			if ((!ignoreXPosts) && (!MobileDevice.isMobileDevice(request))) {
				int originalMessageCount = Integer.parseInt(request.getParameter("messageCount"));
				boolean xPosts = this.hasXPosts(thread, originalMessageCount);

				if (xPosts == true) {
					String url = "/ThreadSelectedControllerServlet?boardid=" + boardId + "&threadid=" + threadId + "&originalmessagecount=" + originalMessageCount;
					RequestDispatcher dispatcher = request.getRequestDispatcher(url);
					request.setAttribute("body", text);
					dispatcher.forward(request, response);
					return;
				}
			}

			MessageFloodChecker.check(user);

			/* Add the new message into the database */

			Message message = new Message(thread);
			message.setBoardId(boardId);
			message.setThreadId(threadId);
			message.setTimestamp(now);
			message.setUserId(user.getLoginId());
			message.setDisplayName(user.getFullDisplayName());
			message.setIpAddress(IPAddress.getIPAddress(request));
			message.setBody(text);

			/*
			 * Anonymous admins don't write details to the database or anywhere
			 * else, so override the actual details in the message, but only if
			 * they choose to use the anonymous admin.
			 */

			if (user.isBoardAdmin(boardId) && (adminId != null) && (!adminId.equals(user.getDisplayName()))) {
				message.setDisplayName(adminId);
				message.setIpAddress("127.0.0.1");
				int number = adminId.hashCode();
				String loginId = adminId.replace(" ", "") + number + "@hotmail.com";
				message.setUserId(loginId);
			}

			thread.addMessage(user, message, user.getLoginId(), false);

			/*
			 * Behaviour here has to differ. If you are a mobile device, we
			 * always ignore x-posts, so just forwarding to the referrer is good
			 * and keeps the GET parameters that instruct the site to process
			 * via XSLT. If you're not mobile, then we might have gone through
			 * an x-post page, so the referrer will actually be
			 * NewAnswerControllerServlet, which we don't want to forward to, so
			 * explicitly redirect to ThreadSelected.
			 */

			if (MobileDevice.isMobileDevice(request)) {
				response.sendRedirect(referrer);
				return;
			} else {

				/*
				 * If it's the mobile interface, tack on the XML and stylesheet
				 * parameters
				 */

				String url = request.getContextPath() + "/ThreadSelectedControllerServlet?boardid=" + boardId + "&threadid=" + threadId;

				if (mobileInterface) {
					url += additionalParameters;
				}

				url += "#unread";

				response.sendRedirect(url);
			}

		} catch (DuplicateMessageException dme) {

			request.setAttribute("message", "You have tried to insert a duplicate message.");
			RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
			dispatcher.forward(request, response);

		} catch (NoSuchThreadException e) {
			/* Do nothing. Probably just a thread that's been moved */
		} catch (Throwable t) {

			String header = "Throwable caught on NewAnswerControllerServlet. Board: " + boardId + " and Thread: " + threadId + " posted by user " + yourName + " at: " + now + "\n\n";

			StringWriter writer = new StringWriter();
			t.printStackTrace(new PrintWriter(writer));
			user = User.getOnlyIfInCache(yourName);
			Logger.log(now, header + writer.toString(), user);
			throw new ServletException(t);

		}

	}

}
